package com.hilin.base.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hilin.base.conf.ServerConfig;
import com.hilin.base.dao.mapper.IAccountMapper;
import com.hilin.base.dao.mapper.IOrgMapper;
import com.hilin.common.common.ReturnCode;
import com.hilin.common.util.AssertUtils;
import com.hilin.common.util.EncryptUtils;
import com.hilin.common.util.IdUtils;
import com.hilin.common.util.RandomUtils;
import com.hilin.entity.constant.RedisKeys;
import com.hilin.entity.em.LoginOpType;
import com.hilin.entity.po.AccountInfoPo;
import com.hilin.entity.po.OrgInfoPo;
import com.hilin.entity.po.SysAccessPo;
import com.hilin.entity.po.SysRolePo;
import com.hilin.entity.vo.SignVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class AccountService {

    @Autowired
    private RedisTemplate<String, SignVo> accountInfoPoRedisTemplate;

    @Autowired
    private IAccountMapper iAccountMapper;

    @Autowired
    private IOrgMapper iOrgMapper;

    @Autowired
    private ServerConfig serverConfig;


    public AccountInfoPo create(AccountInfoPo accountInfo) {
        if (ObjectUtils.isEmpty(accountInfo.getAccountId())) {
            accountInfo.setAccountId(IdUtils.DEFAULT.gen().toString());
        }
        if (ObjectUtils.isEmpty(accountInfo.getAccountName())) {
            accountInfo.setAccountName(AccountInfoPo.genAccountName());
        }
        iAccountMapper.insert(accountInfo);
        return accountInfo;
    }

    public AccountInfoPo login(String accountId, String password, Long orgId, LoginOpType opType) {
        QueryWrapper<AccountInfoPo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(AccountInfoPo::getAccountId, AccountInfoPo::getAccountName,
                        AccountInfoPo::getOrgId, AccountInfoPo::getDeptId, AccountInfoPo::getMobile,
                        AccountInfoPo::getPassword, AccountInfoPo::getSalt)
                .eq(AccountInfoPo::getAccountId, accountId)
                .eq(!ObjectUtils.isEmpty(orgId), AccountInfoPo::getOrgId, orgId);
        List<AccountInfoPo> accountInfos = iAccountMapper.selectList(queryWrapper);
        AssertUtils.isNotEmpty(accountInfos, ReturnCode.AUTH_USER_NOT_EXIST.respMsg("用户不存在"));
        AssertUtils.isRex(accountInfos.size() == 1, ReturnCode.AUTH_MULTI_USER.resp());
        AccountInfoPo accountInfo = accountInfos.get(0);

        switch (opType) {

        }

        // 验证密码
        AssertUtils.isRex(accountInfo.getPassword().equals(EncryptUtils.getMd5(accountInfo.getSalt() + password)),
                ReturnCode.AUTH_ACCOUNT_PASSWORD_ERROR.resp());
        accountInfo.setTokenRandomStr(RandomUtils.genRandomStr(6));
        String token = RandomUtils.getABSRandomString();
        accountInfo.setToken(token);
        accountInfoPoRedisTemplate.opsForValue()
                .set(String.format(RedisKeys.RK_USER_TOKEN, token), SignVo.builder()
                        .orgId(orgId).timestamp(System.currentTimeMillis() / 1000)
                        .accountId(accountId).nonce(RandomUtils.genRandomStr(15))
                        .build(), serverConfig.getTokenExp(), TimeUnit.DAYS);
        return accountInfo;
    }

    public List<OrgInfoPo> getAccountOrgList(String accountId) {
        List<OrgInfoPo> orgInfoPos = iAccountMapper.getAccountOrgList(accountId);
        if (orgInfoPos == null) {
            orgInfoPos = new ArrayList<>();
        }
        return orgInfoPos;
    }

    public List<SysAccessPo> getAllAccess(SignVo signVo) {
        return Optional.ofNullable(iAccountMapper.getAllAccess(signVo.getAccountId(), signVo.getOrgId()))
                .orElse(new ArrayList<>());
    }

    public List<SysRolePo> getAllRoles(SignVo signVo) {
        return Optional.ofNullable(iAccountMapper.getAllRole(signVo.getAccountId(), signVo.getOrgId()))
                .orElse(new ArrayList<>());
    }

    public List<SysAccessPo> getRoleAccess(SignVo signVo, Long roleId) {
        List<SysAccessPo> sysAccessPos = iAccountMapper.getRoleAccess(roleId, signVo.getOrgId());
        return Optional.ofNullable(sysAccessPos)
                .orElse(new ArrayList<>());
    }

    @SneakyThrows
    public List<AccountInfoPo> pageList(AccountInfoPo accountInfo) {
        return iAccountMapper.selectList(accountInfo.getQueryWrapper());
    }

    public String serviceGenToken(SignVo signVo) {
        AssertUtils.isRex(Math.abs(signVo.getTimestamp() - System.currentTimeMillis() / 1000) < 300,
                ReturnCode.AUTH_ERROR.respMsg("时间戳错误"));

        OrgInfoPo orgInfo = iOrgMapper.selectById(signVo.getOrgId());
        AssertUtils.isNotEmpty(orgInfo, ReturnCode.DATA_NOT_EXIST.respMsg("机构不存在"));
        AssertUtils.isNotEmpty(orgInfo.getOrgId(), ReturnCode.DATA_NOT_EXIST.respMsg("机构不存在"));
        AssertUtils.isRex(check(signVo, orgInfo.getOrgSecret()), ReturnCode.AUTH_ERROR.resp());
        String token = RandomUtils.getABSRandomString();
        accountInfoPoRedisTemplate.opsForValue()
                .set(String.format(RedisKeys.RK_USER_TOKEN, token), SignVo.builder()
                        .orgId(signVo.getOrgId()).timestamp(System.currentTimeMillis() / 1000)
                        .accountId(signVo.getAccountId()).nonce(RandomUtils.genRandomStr(15))
                        .build(), serverConfig.getTokenExp(), TimeUnit.SECONDS);
        return token;
    }

    private boolean check(SignVo signVo, String secret) {
        String genSign =
                (ObjectUtils.isEmpty(signVo.getAccountId()) ? "" : signVo.getAccountId()) + signVo.getNonce() + signVo.getOrgId() + signVo.getTimestamp() + secret;
        log.warn("输入的签名字符串:{}", signVo.getSign());
        log.warn("认证的原始字符串:{}", genSign);
        log.warn("认证的签名字符串:{}", EncryptUtils.getMd5(genSign));
        return !ObjectUtils.isEmpty(signVo.getSign()) &&
                signVo.getSign().equalsIgnoreCase(EncryptUtils.getMd5(genSign));
    }
}
